package com.luobr.backend.manage.websocket;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.luobr.backend.manage.websocket.disruptor.ImageEditEventProducer;
import com.luobr.backend.manage.websocket.model.ImageEditRequestMessage;
import com.luobr.backend.manage.websocket.model.ImageEditResponseMessage;
import com.luobr.backend.manage.websocket.model.enums.ImageEditActionEnum;
import com.luobr.backend.manage.websocket.model.enums.ImageEditMessageTypeEnum;
import com.luobr.backend.model.vo.user.UserLoginVO;
import com.sun.istack.internal.NotNull;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class ImageEditHandler extends TextWebSocketHandler {

    @Resource
    private ImageEditEventProducer imageEditEventProducer;

    // 每张图片的编辑状态，key: imageId, value: 当前正在编辑的用户 ID
    private final Map<Long, Long> imageEditingUsers = new ConcurrentHashMap<>();

    // 保存所有连接的会话，key: imageId, value: 用户会话集合
    private final Map<Long, Set<WebSocketSession>> imageSessions = new ConcurrentHashMap<>();

    /**
     * 连接建立
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 保存会话到集合中
        UserLoginVO user = (UserLoginVO) session.getAttributes().get("user");
        Long imageId = (Long) session.getAttributes().get("imageId");
        imageSessions.putIfAbsent(imageId, ConcurrentHashMap.newKeySet());
        imageSessions.get(imageId).add(session);

        // 构造响应
        ImageEditResponseMessage imageEditResponseMessage = new ImageEditResponseMessage();
        imageEditResponseMessage.setType(ImageEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s 加入编辑", user.getUserName());
        imageEditResponseMessage.setMessage(message);
        imageEditResponseMessage.setUser(user);
        // 广播给编辑给同一张图片的用户
        broadcastToImage(imageId, imageEditResponseMessage);
    }

    /**
     * 接收客户端的消息并做出不同的处理
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        // 将消息解析为 PictureEditMessage
        ImageEditRequestMessage imageEditRequestMessage = JSONUtil.toBean(message.getPayload(), ImageEditRequestMessage.class);
        // 从 Session 属性中获取公共参数
        Map<String, Object> attributes = session.getAttributes();
        UserLoginVO user = (UserLoginVO) attributes.get("user");
        Long imageId = (Long) attributes.get("imageId");
        // 生产消息
        imageEditEventProducer.publishEvent(imageEditRequestMessage, session, user, imageId);
    }

    /**
     * 向客户端发送开始编辑操作
     */
    public void handleEnterEditMessage(ImageEditRequestMessage imageEditRequestMessage, WebSocketSession session, UserLoginVO user, Long imageId) throws Exception {
        // 没有用户正在编辑该图片，才能进入编辑
        if (!imageEditingUsers.containsKey(imageId)) {
            // 设置当前用户为编辑用户
            imageEditingUsers.put(imageId, user.getId());
            // 发送消息
            ImageEditResponseMessage imageEditResponseMessage = new ImageEditResponseMessage();
            imageEditResponseMessage.setType(ImageEditMessageTypeEnum.ENTER_EDIT.getValue());
            String message = String.format("%s 开始编辑图片", user.getUserName());
            imageEditResponseMessage.setMessage(message);
            imageEditResponseMessage.setUser(user);
            broadcastToImage(imageId, imageEditResponseMessage);
        }
    }

    /**
     * 向客户端发送每个操作信息（放大、缩小等）
     */
    public void handleEditActionMessage(ImageEditRequestMessage imageEditRequestMessage, WebSocketSession session, UserLoginVO user, Long imageId) throws Exception {
        Long editingUserId = imageEditingUsers.get(imageId);
        String editAction = imageEditRequestMessage.getEditAction();
        ImageEditActionEnum actionEnum = ImageEditActionEnum.getEnumByValue(editAction);
        if (actionEnum == null) {
            return;
        }
        // 确认是当前编辑者
        if (editingUserId != null && editingUserId.equals(user.getId())) {
            ImageEditResponseMessage imageEditResponseMessage = new ImageEditResponseMessage();
            imageEditResponseMessage.setType(ImageEditMessageTypeEnum.EDIT_ACTION.getValue());
            String message = String.format("%s 执行 %s", user.getUserName(), actionEnum.getText());
            imageEditResponseMessage.setMessage(message);
            imageEditResponseMessage.setEditAction(editAction);
            imageEditResponseMessage.setUser(user);
            // 广播给除了当前客户端之外的其他用户，否则会造成重复编辑
            broadcastToImage(imageId, imageEditResponseMessage, session);
        }
    }

    /**
     * 处理退出编辑消息
     */
    public void handleExitEditMessage(ImageEditRequestMessage imageEditRequestMessage, WebSocketSession session, UserLoginVO user, Long imageId) throws Exception {
        Long editingUserId = imageEditingUsers.get(imageId);
        if (editingUserId != null && editingUserId.equals(user.getId())) {
            // 移除当前用户的编辑状态
            imageEditingUsers.remove(imageId);
            // 构造响应，发送退出编辑的消息通知
            ImageEditResponseMessage imageEditResponseMessage = new ImageEditResponseMessage();
            imageEditResponseMessage.setType(ImageEditMessageTypeEnum.EXIT_EDIT.getValue());
            String message = String.format("%s 退出编辑图片", user.getUserName());
            imageEditResponseMessage.setMessage(message);
            imageEditResponseMessage.setUser(user);
            broadcastToImage(imageId, imageEditResponseMessage);
        }
    }

    /**
     * 连接关闭之后
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, @NotNull CloseStatus status) throws Exception {
        Map<String, Object> attributes = session.getAttributes();
        Long pictureId = (Long) attributes.get("pictureId");
        UserLoginVO user = (UserLoginVO) attributes.get("user");
        // 移除当前用户的编辑状态
        handleExitEditMessage(null, session, user, pictureId);

        // 删除会话
        Set<WebSocketSession> sessionSet = imageSessions.get(pictureId);
        if (sessionSet != null) {
            sessionSet.remove(session);
            if (sessionSet.isEmpty()) {
                imageSessions.remove(pictureId);
            }
        }

        // 响应
        ImageEditResponseMessage imageEditResponseMessage= new ImageEditResponseMessage();
        imageEditResponseMessage.setType(ImageEditMessageTypeEnum.INFO.getValue());
        String message = String.format("%s 离开编辑", user.getUserName());
        imageEditResponseMessage.setMessage(message);
        imageEditResponseMessage.setUser(user);
        broadcastToImage(pictureId, imageEditResponseMessage);
    }

    /**
     * 广播消息（排除自己或者部分其他会话）
     */
    private void broadcastToImage(Long imageId, ImageEditResponseMessage ImageEditResponseMessage, WebSocketSession excludeSession) throws Exception {
        Set<WebSocketSession> sessionSet = imageSessions.get(imageId);
        if (CollUtil.isNotEmpty(sessionSet)) {
            // 序列化为 JSON 字符串
            String message = this.getObjectMapper().writeValueAsString(ImageEditResponseMessage);
            TextMessage textMessage = new TextMessage(message);
            for (WebSocketSession session : sessionSet) {
                // 排除掉的 session 不发送
                if (excludeSession != null && excludeSession.equals(session)) {
                    continue;
                }
                if (session.isOpen()) {
                    session.sendMessage(textMessage);
                }
            }
        }
    }

    /**
     * 全部广播
     */
    private void broadcastToImage(Long imageId, ImageEditResponseMessage ImageEditResponseMessage) throws Exception {
        broadcastToImage(imageId, ImageEditResponseMessage, null);
    }

    /**
     * 配置序列化：将 Long 类型转为 String，解决丢失精度问题
     */
    public ObjectMapper getObjectMapper() {
        // 创建 ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();
        // 配置序列化：将 Long 类型转为 String，解决丢失精度问题
        SimpleModule module = new SimpleModule();
        module.addSerializer(Long.class, ToStringSerializer.instance);
        module.addSerializer(Long.TYPE, ToStringSerializer.instance); // 支持 long 基本类型
        objectMapper.registerModule(module);
        return objectMapper;
    }
}
